package com.rgs.gateway.authentication.server.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rgs.gateway.authentication.server.entity.bo.NewMvcRequestMatcherBO;
import com.rgs.gateway.authentication.server.entity.po.ResourcePO;
import com.rgs.gateway.authentication.server.mapper.ResourceServiceMapper;
import com.rgs.gateway.authentication.server.service.IResourceService;
import com.rgs.gateway.common.enums.YesOrNoEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ResourceServiceImpl extends ServiceImpl<ResourceServiceMapper, ResourcePO> implements IResourceService {

    @Autowired
    private HandlerMappingIntrospector mvcHandlerMappingIntrospector;

    @Autowired
    private ResourceServiceMapper resourceServiceMapper;

    /**
     * 系统中所有权限集合
     */
    private static final Map<RequestMatcher, ConfigAttribute> resourceConfigAttributes = new HashMap<>();

    @Override
    public synchronized void saveResource(ResourcePO resource) {
        resourceConfigAttributes.put(this.newMvcRequestMatcher(resource), new SecurityConfig(String.valueOf(resource.getId())));
        log.info("resourceConfigAttributes size:{}", resourceConfigAttributes.size());
    }

    @Override
    public synchronized void removeResource(ResourcePO resource) {
        resourceConfigAttributes.remove(resource.getId());
        log.info("resourceConfigAttributes size:{}", resourceConfigAttributes.size());
    }

    @Override
    public synchronized void loadResource() {
        QueryWrapper<ResourcePO> wrapper = new QueryWrapper<>();
        wrapper.select("DISTINCT *").lambda().eq(ResourcePO::getIsDelete, YesOrNoEnum.NO.getCodeValue());
        //获取所有资源信息
        List<ResourcePO> resources = this.list(wrapper);
        log.info("加载的所有资源信息为：{}", resources.toString());
        Map<MvcRequestMatcher, SecurityConfig> tempResourceConfigAttributes = resources.stream()
                .collect(Collectors.toMap(resource -> this.newMvcRequestMatcher(resource), resource -> new SecurityConfig(String.valueOf(resource.getId()))));
        resourceConfigAttributes.clear();
        resourceConfigAttributes.putAll(tempResourceConfigAttributes);
        log.info("init resourceConfigAttributes:{}", resourceConfigAttributes);
    }

    /**
     * 从资源池获取匹配资源信息
     *
     * @param authRequest
     * @return
     */
    @Override
    public ConfigAttribute findConfigAttributesByUrl(HttpServletRequest authRequest) {
        return resourceConfigAttributes.keySet().stream()
                .filter(requestMatcher -> requestMatcher.matches(authRequest))
                .map(requestMatcher -> resourceConfigAttributes.get(requestMatcher))
                .peek(urlConfigAttribute -> log.debug("url在资源池中配置：{}", urlConfigAttribute.getAttribute()))
                .findFirst()
                .orElse(new SecurityConfig("NONEXISTENT_URL"));
    }

    /**
     * 通过用户信息获取所拥有资源
     *
     * @param appKey
     * @return
     */
    @Override
    public Set<ResourcePO> queryByAppKey(String appKey) {
        return resourceServiceMapper.getResourceByAppKey(appKey);
    }

    /**
     * 创建请求资源
     *
     * @param resource 资源数据库实体对象
     * @return
     */
    private MvcRequestMatcher newMvcRequestMatcher(ResourcePO resource) {
        return new NewMvcRequestMatcherBO(mvcHandlerMappingIntrospector, resource.getResourcePath(), resource.getResourceMethod().toUpperCase());
    }
}